package com.ssy.lingxi.order.serviceimpl.base;

import com.ssy.lingxi.common.constant.order.OrderTradeProcessTypeEnum;
import com.ssy.lingxi.common.constant.product.PriceTypeEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.order.entity.BaseTradeProcessDO;
import com.ssy.lingxi.order.entity.OrderTradeProcessDO;
import com.ssy.lingxi.order.entity.OrderTradeProcessProductDO;
import com.ssy.lingxi.order.model.bo.OrderProductBO;
import com.ssy.lingxi.order.model.vo.process.request.OrderTradeProcessProductVO;
import com.ssy.lingxi.order.model.vo.process.response.OrderTradeProcessProductQueryVO;
import com.ssy.lingxi.order.repository.OrderTradeProcessProductRepository;
import com.ssy.lingxi.order.service.base.IBaseOrderTradeProcessProductService;
import com.ssy.lingxi.order.utils.NumberUtil;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 交易流程关联的商品相关接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2021-07-25
 */
@Service
public class BaseOrderTradeProcessProductServiceImpl implements IBaseOrderTradeProcessProductService {
    @Resource
    private OrderTradeProcessProductRepository orderTradeProcessProductRepository;

    /**
     * 校验交易流程类型与商品价格类型的配置关系
     *
     * @param baseTradeProcess   基础的交易流程
     * @param includeAllProducts 是否包含所有商品
     * @param productList        商品列表
     * @return 校验结果
     */
    @Override
    public Wrapper<Void> checkTradeProcessProducts(BaseTradeProcessDO baseTradeProcess, Boolean includeAllProducts, List<OrderTradeProcessProductVO> productList) {
        //商品不能为空
        if(!includeAllProducts && CollectionUtils.isEmpty(productList)) {
            return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_PRODUCTS_CAN_NOT_BE_EMPTY);
        }

        OrderTradeProcessTypeEnum tradeProcessType = OrderTradeProcessTypeEnum.parse(baseTradeProcess.getProcessType());
        if(tradeProcessType == null) {
            return Wrapper.fail(ResponseCode.ORDER_BASE_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        switch (tradeProcessType) {
            //订单交易流程中不能配置积分兑换商品
            case ORDER_TRADE:
                if(!includeAllProducts && productList.stream().anyMatch(product -> product.getPriceType().equals(PriceTypeEnum.Score.getCode()))) {
                    return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_CAN_NOT_CONTAINS_ANY_SCORE_PRODUCT);
                }
                break;
            //积分兑换流程只能配置积分兑换商品
            case RIGHT_POINT:
                if(!includeAllProducts && productList.stream().anyMatch(product -> !product.getPriceType().equals(PriceTypeEnum.Score.getCode()))) {
                    return Wrapper.fail(ResponseCode.ORDER_TRADE_RIGHT_POINT_PROCESS_CAN_ONLY_CONTAINS_SCORE_PRODUCT);
                }
                break;
            //积分兑换商品不支持售后退货
            case AFTER_SALES_RETURNS:
                if(!includeAllProducts && productList.stream().anyMatch(product -> product.getPriceType().equals(PriceTypeEnum.Score.getCode()))) {
                    return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_AFTER_SALES_RETURNS_CAN_NOT_CONTAINS_ANY_SCORE_PRODUCT);
                }
                break;
            default:
                break;
        }

        return Wrapper.success();
    }

    /**
     * 校验交易流程关联的商品
     *
     * @param tradeProcesses 已经存在的同类型的交易规则配置
     * @param includeAllProducts 是否包含所有商品
     * @param productList        商品列表
     * @return 校验结果
     */
    @Override
    public Wrapper<Void> checkProducts(List<OrderTradeProcessDO> tradeProcesses, Boolean includeAllProducts, List<OrderTradeProcessProductVO> productList) {
        //如果适用于所有商品，查询是否有另一流程也适用于所有商品，或商品不为空
        if(includeAllProducts) {
            if(CollectionUtils.isEmpty(tradeProcesses)) {
                return Wrapper.success();
            }

            if(tradeProcesses.stream().anyMatch(OrderTradeProcessDO::getAllProducts)) {
                return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_ALL_PRODUCTS_EXIST);
            }

            if(tradeProcesses.stream().anyMatch(process -> !CollectionUtils.isEmpty(process.getProducts()))) {
                return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_EXIST_WITH_PRODUCTS);
            }
        } else {
            //校验商品Id+skuId是否有重复
            if(productList.size() != productList.stream().map(p -> new OrderProductBO(p.getProductId(), p.getSkuId())).distinct().count()) {
                return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_PRODUCT_ID_DUPLICATE);
            }

            if(CollectionUtils.isEmpty(tradeProcesses)) {
                return Wrapper.success();
            }

            if(tradeProcesses.stream().anyMatch(OrderTradeProcessDO::getAllProducts)) {
                return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_ALL_PRODUCTS_EXIST);
            }

            //判断商品是否存在于另一个流程中
            Specification<OrderTradeProcessProductDO> specification = (Specification<OrderTradeProcessProductDO>) (root, query, criteriaBuilder) -> {
                List<Predicate> list = new ArrayList<>();
                list.add(root.get("process").as(OrderTradeProcessDO.class).in(tradeProcesses));

                //or查询
                List<Predicate> orList = new ArrayList<>();
                productList.forEach(product -> orList.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("productId").as(Long.class), product.getProductId()), criteriaBuilder.equal(root.get("skuId").as(Long.class), product.getSkuId()))));
                Predicate[] orPredicates = new Predicate[orList.size()];
                list.add(criteriaBuilder.or(orList.toArray(orPredicates)));

                Predicate[] p = new Predicate[list.size()];
                return criteriaBuilder.and(list.toArray(p));
            };

            if(orderTradeProcessProductRepository.count(specification) > 0) {
                return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_PRODUCT_EXIST_ANOTHER_PROCESS);
            }
        }

        return Wrapper.success();
    }

    /**
     * 校验交易流程关联的商品，调用方要保存OrderTradeProcessDO
     *
     * @param tradeProcess       交易流程配置
     * @param includeAllProducts 是否包含所有商品
     * @param productList        商品列表
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void saveProducts(OrderTradeProcessDO tradeProcess, Boolean includeAllProducts, List<OrderTradeProcessProductVO> productList) {
        if(includeAllProducts) {
            tradeProcess.setProducts(new HashSet<>());
            return;
        }

        //保存
        List<OrderTradeProcessProductDO> products = productList.stream().map(p -> {
            OrderTradeProcessProductDO product = new OrderTradeProcessProductDO();
            product.setProcess(tradeProcess);
            product.setProductId(p.getProductId());
            product.setSkuId(p.getSkuId());
            product.setName(p.getName());
            product.setCategory(p.getCategory());
            product.setBrand(StringUtils.hasLength(p.getBrand()) ? p.getBrand() : "");
            product.setPriceType(p.getPriceType());
            product.setProductType(NumberUtil.isNullOrNegative(p.getProductType()) ? 0 : p.getProductType());
            return product;
        }).collect(Collectors.toList());

        orderTradeProcessProductRepository.saveAll(products);

        // 设置关联，在外要保存OrderTradeProcessDO
        tradeProcess.setProducts(new HashSet<>(products));
    }

    /**
     * 分页查询交易流程关联的商品列表
     *
     * @param tradeProcess 交易流程规则
     * @param name         商品名称
     * @param current      当前页
     * @param pageSize     每页行数
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<OrderTradeProcessProductQueryVO>> pageProducts(OrderTradeProcessDO tradeProcess, String name, int current, int pageSize) {
        Pageable pageable = PageRequest.of(current - 1, pageSize, Sort.by("id").ascending());
        Specification<OrderTradeProcessProductDO> specification = (Specification<OrderTradeProcessProductDO>) (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("process").as(OrderTradeProcessDO.class), tradeProcess.getId()));

            if(StringUtils.hasLength(name)) {
                list.add(criteriaBuilder.like(root.get("name").as(String.class), "%" + name.trim() + "%"));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        Page<OrderTradeProcessProductDO> pageList = orderTradeProcessProductRepository.findAll(specification, pageable);
        return Wrapper.success(new PageData<>(pageList.getTotalElements(), pageList.getContent().stream().map(product ->{
            OrderTradeProcessProductQueryVO queryVO = new OrderTradeProcessProductQueryVO();
            queryVO.setProductId(product.getProductId());
            queryVO.setSkuId(product.getSkuId());
            queryVO.setName(product.getName());
            queryVO.setCategory(product.getCategory());
            queryVO.setBrand(product.getBrand());
            queryVO.setPriceType(product.getPriceType());
            queryVO.setPriceTypeName(PriceTypeEnum.getNameByCode(product.getPriceType()));
            queryVO.setProductType(NumberUtil.isNullOrZero(product.getProductType()) ? 0 : product.getProductType());
            return queryVO;
        }).collect(Collectors.toList())));
    }

    /**
     * 更新交易流程关联的商品，调用方要保存OrderTradeProcessDO
     *
     * @param tradeProcess       交易流程配置
     * @param includeAllProducts 是否包含所有商品
     * @param productList        商品列表
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void updateProducts(OrderTradeProcessDO tradeProcess, Boolean includeAllProducts, List<OrderTradeProcessProductVO> productList) {
        if(includeAllProducts) {
            orderTradeProcessProductRepository.deleteByProcess(tradeProcess);
            tradeProcess.setProducts(new HashSet<>());
            return;
        }

        // 先删除，再保存
        orderTradeProcessProductRepository.deleteByProcess(tradeProcess);

        List<OrderTradeProcessProductDO> products = productList.stream().map(p -> {
            OrderTradeProcessProductDO product = new OrderTradeProcessProductDO();
            product.setProcess(tradeProcess);
            product.setProductId(p.getProductId());
            product.setSkuId(p.getSkuId());
            product.setName(p.getName());
            product.setCategory(p.getCategory());
            product.setBrand(StringUtils.hasLength(p.getBrand()) ? p.getBrand() : "");
            product.setPriceType(p.getPriceType());
            product.setProductType(p.getProductType());
            return product;
        }).collect(Collectors.toList());

        orderTradeProcessProductRepository.saveAll(products);

        // 设置关联，在外要保存OrderTradeProcessDO
        tradeProcess.setProducts(new HashSet<>(products));
    }
}
